/*
*  This file is part of mangusta
*
*  mangusta is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  mangusta is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with mangusta; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
*
*  Author: <antonino.calderone@ericsson.com>, <acaldmail@gmail.com>
*
*/


/* -------------------------------------------------------------------------- */

#include "nu_variant.h"
#include "nu_functions.h"
#include "nu_builtin_help.h"
#include "nu_lang_defs.h"

#include <functional>
#include <string>
#include <cstdlib>


/* -------------------------------------------------------------------------- */

namespace nu
{


/* -------------------------------------------------------------------------- */

#define THIS_MODULE "string"
#define MD_NAMESPACE THIS_MODULE "."


/* -------------------------------------------------------------------------- */

static struct module_string_t : public module_t
{
   module_string_t()
   {
      add_module(this, THIS_MODULE);
   }

   virtual bool load() throw() override
   {
      auto & hmap = builtin_help_t::get_instance();

      hmap[ {lang_item_t::MODULE, THIS_MODULE}] =
      {
         "The string module provides access to the string manipulation functions",
         THIS_MODULE" module"
      };

      functions_t & fmap = functions_t::get_instance();


      struct _strlen_t
      {
         int operator()(const std::string x) throw()
         {
            return int(x.size());
         }
      };

      fmap[MD_NAMESPACE"len"] = functor_int_string < _strlen_t > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE"len"}] =
      {
         "Returns the number of characters in s",
         PROGLANG_TYPE_INTEGER " " MD_NAMESPACE "len(" PROGLANG_TYPE_STRING " s)"
      };

      struct _ascii_str
      {
         int operator()(const std::string x) throw()
         {
            return (x.empty() ? 0 : x.c_str()[0]) & 0xff;
         }
      };

      fmap[MD_NAMESPACE "ascii"] = functor_int_string < _ascii_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "ascii"}] =
      {
         "Returns a numeric value that is the ASCII code "
         "for the first character of the string s",
         PROGLANG_TYPE_INTEGER " " MD_NAMESPACE "ascii(" PROGLANG_TYPE_STRING " s)"
      };


      struct _space_str
      {
         std::string operator()(int nspace) throw()
         {
            std::string s;
            nspace = nspace < 0 ? 0 : nspace;

            for (int i = 0; i < nspace; ++i)
               s += " ";

            return s;
         }
      };

      //functor_string_int
      fmap[MD_NAMESPACE "space"] = functor_string_int < _space_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "space"}] =
      {
         "Returns a string of n spaces",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "space(" PROGLANG_TYPE_INTEGER " n)"
      };


      struct _chr_str
      {
         std::string operator()(int code) throw()
         {
            char s[2] = { 0 };
            s[0] = code;
            return s;
         }
      };

      fmap[MD_NAMESPACE "chr"] = functor_string_int < _chr_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "chr"}] =
      {
         "Returns the character associated with the specified character code.",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "chr(" PROGLANG_TYPE_INTEGER " code)"
      };


      struct _left_str
      {
         std::string operator()(const std::string& s, int n) throw()
         {
            if (n <= 0)
               return "";

            else if (n > int(s.size()))
               n = int(s.size());

            return s.substr(0, n);
         }
      };

      fmap[MD_NAMESPACE "left"] = functor_string_string_int < _left_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "left"}] =
      {
         "Returns a string that comprises the left-most n characters of s",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "left("
         PROGLANG_TYPE_STRING " s, "
         PROGLANG_TYPE_INTEGER " n)"
      };

      struct _lcase_str
      {
         std::string operator()(const std::string& s) throw()
         {
            std::string ret = s;
            std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
            return ret;
         }
      };

      fmap[MD_NAMESPACE "lcase"] = functor_string_string < _lcase_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "lcase"}] =
      {
         "Converts a string to lower-case.",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "lcase(" PROGLANG_TYPE_STRING " s)"
      };


      struct _ucase_str
      {
         std::string operator()(const std::string& s) throw()
         {
            std::string ret = s;
            std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper);
            return ret;
         }
      };

      fmap[MD_NAMESPACE "ucase"] = functor_string_string < _ucase_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "ucase"}] =
      {
         "Converts a string to upper-case.",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "ucase(" PROGLANG_TYPE_STRING " s)"
      };


      struct _right_str
      {
         std::string operator()(const std::string& s, int n) throw()
         {
            if (n <= 0)
               return s;

            else if (n > int(s.size()))
               n = int(s.size());

            int skip = int(s.size()) - n;

            return s.substr(skip, n);
         }
      };

      fmap[MD_NAMESPACE "right"] = functor_string_string_int < _right_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "right"}] =
      {
         "Returns a string that comprises the right-most n characters of s",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "right("
         PROGLANG_TYPE_STRING " s, "
         PROGLANG_TYPE_INTEGER " n)"
      };


      struct _instrcs
      {
         int operator()(
            const std::string& s,
            const std::string& search_str) throw()
         {
            if (search_str.empty())
               return 0;

            else if (s.empty() || search_str.size() > s.size())
               return -1;

            else if (search_str.size() == s.size() && s == search_str)
               return 0;

            std::string mystr = s;
            int pos = 0;

            while (search_str.size() <= mystr.size())
            {
               if (mystr.substr(0, search_str.size()) == search_str)
                  return pos;

               ++pos;
               mystr = mystr.substr(1, mystr.size() - 1);
            }

            return -1;
         }
      };

      fmap[MD_NAMESPACE "instrcs"] = functor_int_string_string < _instrcs > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "instrcs"}] =
      {
         "Returns an integer specifying the start position\n"
         "of the first occurrence of one string within another.\n"
         "Returns -1 if searchstr is not a substring of mystr\n"
         "This function is case sensitive.\n",
         PROGLANG_TYPE_INTEGER " " MD_NAMESPACE "instrcs("
         PROGLANG_TYPE_STRING " mystr, "
         PROGLANG_TYPE_STRING " searchstr)"
      };

      struct _instr : public _instrcs
      {
         int operator()(
            const std::string& s,
            const std::string& search_str) throw()
         {
            std::string mys = s;
            std::string ss = search_str;
            std::transform(mys.begin(), mys.end(), mys.begin(), toupper);
            std::transform(ss.begin(), ss.end(), ss.begin(), toupper);

            return _instrcs::operator()(mys, ss);
         }
      };

      fmap[MD_NAMESPACE "instr"] = functor_int_string_string < _instr > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "instr"}] =
      {
         "Returns an integer specifying the start position\n"
         "of the first occurrence of one string within another.\n"
         "Returns -1 if searchstr is not a substring of mystr\n"
         "This function is case insensitive.\n",
         PROGLANG_TYPE_INTEGER " " MD_NAMESPACE "instr("
         PROGLANG_TYPE_STRING " mystr, "
         PROGLANG_TYPE_STRING " searchstr)"
      };

      struct _sub_str
      {
         //substr(str,pos,n)
         //return a string of n characters from s$ beginning
         //with the pos-th character (0 is first character)
         //Comment: pos >= 0, n >= 0

         std::string operator()(const std::string& s, int pos, int n) throw()
         {
            if (pos < 1)
               pos = 0;

            if (pos >= int(s.size()))
               return "";

            if (n < 0)
               n = 0;

            if ((pos + n) >= int(s.size()))
               n = int(s.size()) - pos;

            return s.substr(pos, n);
         }
      };


      fmap[MD_NAMESPACE "substr"] =
         functor_string_string_int_int < _sub_str > ;


      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "substr"}] =
      {
         "Returns a string of m characters from s beginning with the "
         "n-th character",
         PROGLANG_TYPE_STRING " "
         MD_NAMESPACE "substr("
         PROGLANG_TYPE_STRING  " s, "
         PROGLANG_TYPE_INTEGER " n, "
         PROGLANG_TYPE_INTEGER " m )"
      };


      struct _str2double_t
      {
         double operator()(const std::string& x) throw()
         {
            try
            {
               return std::stod(x);
            }

            catch (...)
            {
               return 0.0;
            }
         }
      };


      fmap[MD_NAMESPACE "stod"] = functor_double_string < _str2double_t > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "stod"}] =
      {
         "Returns the double value of string s",
         PROGLANG_TYPE_DOUBLE " " MD_NAMESPACE "stod(" PROGLANG_TYPE_STRING " s)"
      };


      struct _to_str
      {
         std::string operator()(double x) throw()
         {
            if (::floor(x) == x)
            {
               return to_string(int(x));
            }

            return to_string(x);
         }
      };


      fmap[MD_NAMESPACE "to_string"] = functor_string_double < _to_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "to_string"}] =
      {
         "Returns a string representation of the value of v",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "to_string(" PROGLANG_TYPE_DOUBLE " v)"
      };


      struct _to_hex_str
      {
         std::string operator()(double x) throw()
         {
            std::stringstream ss;
            ss << std::hex << int(x);
            return ss.str();
         }
      };


      fmap[MD_NAMESPACE "hex"] = functor_string_int < _to_hex_str > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "hex"}] =
      {
         "Returns a hexadecimal representation of the value of x",
         PROGLANG_TYPE_STRING " " MD_NAMESPACE "hex(" PROGLANG_TYPE_INTEGER " x)"
      };

      return true;
   }


   /* -------------------------------------------------------------------------- */

}
module_instance;


}// nu

